/*
unsafe

unsafe包说明
Package unsafe 包含一些可以绕过类型安全的操作。
导入 unsafe 的包可能是不可移植( non-portable) 的，而且不受 Go 1 兼容性准则的保护

unsafe 包提供了 2 点重要的能力：
- 任何类型的指针和 unsafe.Pointer 可以相互转换。
- uintptr 类型和 unsafe.Pointer 可以相互转换。

参考资料
https://www.cnblogs.com/qcrao-2018/p/10964692.html
*/

package main

import (
	"fmt"
	"time"
	"unsafe"
)

func main() {
	m0()

	m1_xxxof()

	m2_ops()

	m3_slice()
}

func m0() {
	var a int8 = -1
	fmt.Println(a)
	fmt.Println(uint8(a))
	var k = (*uint8)(unsafe.Pointer(&a))
	fmt.Println(*k)
}

func m1_xxxof() {
	obj := UnsafeObj{a: 1, B: "unsafe", C: time.Now()}
	//obj := []int{1, 2, 3}

	var a uintptr
	a = unsafe.Sizeof(obj.a)
	fmt.Println("uintptr ", a)

	// 返回类型 x 所占据的字节数，但不包含 x 所指向的内容的大小
	fmt.Println("Sizeof:", unsafe.Sizeof(obj.a), unsafe.Sizeof(obj.B), unsafe.Sizeof(obj.C), unsafe.Sizeof(obj.Ptr)) // 8 16 24 8

	// 返回结构体成员在内存中的位置离结构体起始处的字节数，所传参数必须是结构体的成员。
	fmt.Println("Offsetof:", unsafe.Offsetof(obj.a), unsafe.Offsetof(obj.B), unsafe.Offsetof(obj.C), unsafe.Offsetof(obj.Ptr)) // 0 8 24 48

	// 返回 m，m 是指当类型进行内存对齐时，它分配到的内存地址能整除 m。
	fmt.Println("Alignof:", unsafe.Alignof(obj.a), unsafe.Alignof(obj.B), unsafe.Alignof(obj.C), unsafe.Alignof(obj.Ptr)) // 8 8 8 8
}

func m2_ops() {
	// uintptr 可以和 unsafe.Pointer 进行相互转换，uintptr 可以进行数学运算。这样，通过 uintptr 和 unsafe.Pointer 的结合就解决了 Go 指针不能进行数学运算的限制。
	var a uintptr = 10
	var b int = *(*int)(unsafe.Pointer(&a))
	fmt.Println(b)

	var arr = [3]int64{4, 5, 6}
	fmt.Println(arr)
	fmt.Println(unsafe.Sizeof(int64(0))) // 8
	// 转换流程 &arr => pointer => uintptr => pointer => *int64 => int64
	var arr1 = *(*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(&arr)) + unsafe.Sizeof(int64(0))))
	fmt.Println(arr1, arr1 == arr[1])

	// Go 的指针不能进行数学运算
	//var ptr = &arr[0]
	//ptr += 8
	//ptr = &arr[0] + 8
}

func m3_slice() {
	/*
		type slice struct {
			array unsafe.Pointer
			len   int
			cap   int
		}
	*/
	var arr = make([]int, 9, 20)

	var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&arr)) + uintptr(8)))
	fmt.Println(Len, len(arr))

	var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&arr)) + uintptr(16)))
	fmt.Println(Cap, cap(arr))
}
